﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using NET.Tools;
using Puzzler.Plugin.Interface;
using Puzzler.Plugin.Interface.Attributes;

namespace Plugin.Default.SwedenGrid
{
    [PuzzleRenderer]
    public sealed class SwedenGridRenderer : GridPuzzleRenderer<SwedenGridModel, SwedenGridCellInfo>
    {
        private const int ARROW_SIZE = 16;
        private const int ARROW_SIZE_HALF = ARROW_SIZE/2;
        private const int LINED_ARROW_SIZE = 32;
        private const int LINED_ARROW_SIZE_HALF = 16;
        private const double FONT_SIZE_BIG = 48;
        private const double FONT_SIZE_SMALL = 10;

        public SwedenGridRenderer(SwedenGridModel model) : base(model)
        {
        }

        public override IList<Canvas> DrawPuzzleGameViews()
        {
            Canvas canvas = new Canvas
                {
                    Width = Model.GridColumnCount*SwedenGridModel.GRID_SIZE,
                    Height = Model.GridRowCount*SwedenGridModel.GRID_SIZE
                };
            OnDraw(canvas, GridRendererType.Game);

            IList<Canvas> result = new List<Canvas>();
            result.Add(canvas);
            return result;
        }

        public override IList<Canvas> DrawPuzzleSolutionViews()
        {
            Canvas canvas = new Canvas
            {
                Width = Model.GridColumnCount * SwedenGridModel.GRID_SIZE,
                Height = Model.GridRowCount * SwedenGridModel.GRID_SIZE
            };
            OnDraw(canvas, GridRendererType.Solution);

            IList<Canvas> result = new List<Canvas>();
            result.Add(canvas);
            return result;
        }

        protected override void DrawCell(Rect rect, SwedenGridCellInfo cellInfo, Canvas canvas, GridRendererType rendererType)
        {
            if (cellInfo.CellType == SwedenGridCellType.Character)
            {
                DrawCharacterCell(cellInfo, canvas, rect, rendererType);
            }
            else if (cellInfo.CellType == SwedenGridCellType.Question)
            {
                DrawQuestionCell(cellInfo, canvas, rect);
            }
            else
                throw new NotImplementedException();
        }

        protected override void DrawNullCell(Rect rect, Canvas canvas, GridRendererType rendererType)
        {
            //Empty
        }

        protected override void PostDrawCell(Rect rect, SwedenGridCellInfo cellInfo, Canvas canvas, GridRendererType rendererType)
        {
            if (cellInfo.CellType != SwedenGridCellType.Question)
                return;

            if (cellInfo.AdditionalCellContent == null)
            {
                DrawDirectionArrow(cellInfo.CellContent.CellDirection, rect, ArrowDrawType.Single, canvas);
            }
            else
            {
                DrawDirectionArrow(cellInfo.CellContent.CellDirection, rect, ArrowDrawType.DoubleLine1, canvas);
                DrawDirectionArrow(cellInfo.AdditionalCellContent.CellDirection, rect, ArrowDrawType.DoubleLine2, canvas);
            }
        }

        #region Private

        private void DrawQuestionCell(SwedenGridCellInfo cellInfo, Canvas canvas, Rect rect)
        {
            canvas.AddRectangle(rect, new ShapeStyle(
                new StrokeStyle(Model.ColorModel.QuestionBorder),
                new FillStyle(Model.ColorModel.QuestionBackground)));

            if (cellInfo.AdditionalCellContent == null)
            {
                DrawQuestionSingleCellContent(cellInfo, canvas, rect);
            }
            else
            {
                DrawQuestionMultiCellContent(cellInfo, canvas, rect);
            }
        }

        private void DrawQuestionMultiCellContent(SwedenGridCellInfo cellInfo, Canvas canvas, Rect rect)
        {
            #region Top

            TextBlock textBlock1 = canvas.AddText(rect.Left, rect.Top, rect.Width, rect.Height/2,
                                                  cellInfo.CellContent.Text,
                                                  new TextStyle(Model.ColorModel.QuestionText, FONT_SIZE_SMALL,
                                                                false, true, TextAlignment.Left));
            textBlock1.Padding = new Thickness(2.5d);
            canvas.AddLine(rect.Left, rect.Top + rect.Height/2, rect.Right, rect.Top + rect.Height/2,
                           new ShapeStyle(new StrokeStyle(Model.ColorModel.QuestionBorder)));

            #endregion

            #region Bottom

            TextBlock textBlock2 = canvas.AddText(rect.Left, rect.Top + rect.Height/2, rect.Width, rect.Height/2,
                                                  cellInfo.AdditionalCellContent.Text,
                                                  new TextStyle(Model.ColorModel.QuestionText, FONT_SIZE_SMALL,
                                                                false, true, TextAlignment.Left));
            textBlock2.Padding = new Thickness(2.5d);

            #endregion
        }

        private void DrawQuestionSingleCellContent(SwedenGridCellInfo cellInfo, Canvas canvas, Rect rect)
        {
            TextBlock textBlock = canvas.AddText(rect, cellInfo.CellContent.Text,
                                                 new TextStyle(Model.ColorModel.QuestionText, 12, false, true, TextAlignment.Left));
            textBlock.Padding = new Thickness(2.5d);
        }

        private void DrawCharacterCell(SwedenGridCellInfo cellInfo, Canvas canvas, Rect rect, GridRendererType rendererType)
        {
            canvas.AddRectangle(rect, new ShapeStyle(
                new StrokeStyle(Model.ColorModel.RegularBorder),
                new FillStyle(Model.ColorModel.RegularBackground)));

            switch (rendererType)
            {
                case GridRendererType.Editor:
                    {
                        Brush color = cellInfo.ShowAlways ? Model.ColorModel.RegularText : Brushes.Green;
                        canvas.AddText(rect, cellInfo.CellContent.Text, new TextStyle(
                            color, FONT_SIZE_BIG, false, true, TextAlignment.Center));
                    }
                    break;
                case GridRendererType.Game:
                    {
                        if (!cellInfo.ShowAlways)
                            return;

                        canvas.AddText(rect, cellInfo.CellContent.Text, new TextStyle(
                            Model.ColorModel.RegularText, FONT_SIZE_BIG, false, true, TextAlignment.Center));
                    }
                    break;
                case GridRendererType.Solution:
                    {
                        canvas.AddText(rect, cellInfo.CellContent.Text, new TextStyle(
                            Model.ColorModel.RegularText, FONT_SIZE_BIG, false, true, TextAlignment.Center));
                    }
                    break;
                default:
                    throw new NotImplementedException(rendererType.ToString());
            }
        }

        private void DrawDirectionArrow(SwedenGridCellDirection direction, Rect rect, ArrowDrawType type, Canvas canvas)
        {
            Rect arrowRect;

            switch (direction)
            {
                case SwedenGridCellDirection.Left:
                case SwedenGridCellDirection.LineLeftDirectionBottom:
                case SwedenGridCellDirection.LineLeftDirectionTop:
                    switch (type)
                    {
                        case ArrowDrawType.Single:
                            arrowRect = new Rect(rect.Left - ARROW_SIZE, rect.Top + rect.Height/2 - ARROW_SIZE_HALF,
                                                 ARROW_SIZE, ARROW_SIZE);
                            break;
                        case ArrowDrawType.DoubleLine1:
                            arrowRect = new Rect(rect.Left - ARROW_SIZE, rect.Top + rect.Height/4 - ARROW_SIZE_HALF,
                                                 ARROW_SIZE, ARROW_SIZE);
                            break;
                        case ArrowDrawType.DoubleLine2:
                            arrowRect = new Rect(rect.Left - ARROW_SIZE, rect.Top + rect.Height*3/4 - ARROW_SIZE_HALF,
                                                 ARROW_SIZE, ARROW_SIZE);
                            break;
                        default:
                            throw new NotImplementedException(type.ToString());
                    }
                    break;
                case SwedenGridCellDirection.Top:
                case SwedenGridCellDirection.LineTopDirectionRight:
                case SwedenGridCellDirection.LineTopDirectionLeft:
                    arrowRect = new Rect(rect.Left + rect.Width/2 - ARROW_SIZE_HALF, rect.Top - ARROW_SIZE, ARROW_SIZE,
                                         ARROW_SIZE);
                    break;
                case SwedenGridCellDirection.Bottom:
                case SwedenGridCellDirection.LineBottomDirectionRight:
                case SwedenGridCellDirection.LineBottomDirectionLeft:
                    arrowRect = new Rect(rect.Left + rect.Width/2 - ARROW_SIZE_HALF, rect.Bottom, ARROW_SIZE, ARROW_SIZE);
                    break;
                case SwedenGridCellDirection.Right:
                case SwedenGridCellDirection.LineRightDirectionTop:
                case SwedenGridCellDirection.LineRightDirectionBottom:
                    switch (type)
                    {
                        case ArrowDrawType.Single:
                            arrowRect = new Rect(rect.Right, rect.Top + rect.Height/2 - ARROW_SIZE_HALF, ARROW_SIZE,
                                                 ARROW_SIZE);
                            break;
                        case ArrowDrawType.DoubleLine1:
                            arrowRect = new Rect(rect.Right, rect.Top + rect.Height/4 - ARROW_SIZE_HALF, ARROW_SIZE,
                                                 ARROW_SIZE);
                            break;
                        case ArrowDrawType.DoubleLine2:
                            arrowRect = new Rect(rect.Right, rect.Top + rect.Height*3/4 - ARROW_SIZE_HALF, ARROW_SIZE,
                                                 ARROW_SIZE);
                            break;
                        default:
                            throw new NotImplementedException(type.ToString());
                    }
                    break;
                default:
                    throw new NotImplementedException(direction.ToString());
            }

            DrawArrow(direction, arrowRect, canvas);
        }

        private void DrawArrow(SwedenGridCellDirection direction, Rect rect, Canvas canvas)
        {
            ResourceDictionary resource = new ResourceDictionary
                {
                    Source = new Uri("pack://application:,,,/Plugin.Default;component/SwedenGrid/Resources/XAML/Images/Arrows.xaml")
                };

            DrawingImage drawingImage = null;
            switch (direction)
            {
                case SwedenGridCellDirection.Right:
                    drawingImage = (DrawingImage) resource["ArrowRight"];
                    break;
                case SwedenGridCellDirection.Bottom:
                    drawingImage = (DrawingImage) resource["ArrowBottom"];
                    break;
                case SwedenGridCellDirection.Left:
                    drawingImage = (DrawingImage) resource["ArrowLeft"];
                    break;
                case SwedenGridCellDirection.Top:
                    drawingImage = (DrawingImage) resource["ArrowTop"];
                    break;
                case SwedenGridCellDirection.LineTopDirectionRight:
                    drawingImage = (DrawingImage)resource["ArrowLineTopRight"];
                    break;
                case SwedenGridCellDirection.LineTopDirectionLeft:
                    drawingImage = (DrawingImage)resource["ArrowLineTopLeft"];
                    break;
                case SwedenGridCellDirection.LineLeftDirectionBottom:
                    drawingImage = (DrawingImage)resource["ArrowLineLeftBottom"];
                    break;
                case SwedenGridCellDirection.LineLeftDirectionTop:
                    drawingImage = (DrawingImage)resource["ArrowLineLeftTop"];
                    break;
                case SwedenGridCellDirection.LineRightDirectionTop:
                    drawingImage = (DrawingImage)resource["ArrowLineRightTop"];
                    break;
                case SwedenGridCellDirection.LineRightDirectionBottom:
                    drawingImage = (DrawingImage)resource["ArrowLineRightBottom"];
                    break;
                case SwedenGridCellDirection.LineBottomDirectionRight:
                    drawingImage = (DrawingImage)resource["ArrowLineBottomRight"];
                    break;
                case SwedenGridCellDirection.LineBottomDirectionLeft:
                    drawingImage = (DrawingImage)resource["ArrowLineBottomLeft"];
                    break;
                default:
                    throw new NotImplementedException(direction.ToString());
            }

            Image image = new Image
            {
                Source = drawingImage
            };
            canvas.AddFrameworkElement(rect, image);
        }

        #endregion

        #region Nested type: ArrowDrawType

        private enum ArrowDrawType
        {
            Single,
            DoubleLine1,
            DoubleLine2
        }

        #endregion
    }
}